#include "FileUtil.h"
#include "Scintilla.h"

#include <wtypes.h>

HRESULT Sci_ReadFile(const WCHAR* fileName, WCHAR **content, size_t *len, UINT *codepage){
	*len = 0;
	*content = NULL;
	*codepage = SC_CP_ACP;

	HANDLE hFile = CreateFile(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

	if (hFile == INVALID_HANDLE_VALUE) {
		return E_FAIL;
	}

	DWORD dwFileSize = ::GetFileSize(hFile, NULL);
	DWORD dwBytesRead = 0;

    char *data = static_cast<char*>(::HeapAlloc(::GetProcessHeap(), 0, dwFileSize + 1));
	::ReadFile(hFile, data, dwFileSize, &dwBytesRead, NULL);
	data[dwFileSize] = '\0';
	::CloseHandle(hFile);

	UINT cp = SC_CP_ACP;
	WCHAR *wdata = NULL;
	size_t wlen = 0;

	if (dwFileSize >= 3 && strncmp(data, "\xEF\xBB\xBF", 3) == 0){ //UTF8
		cp = SC_CP_UTF8;
		wlen = ::MultiByteToWideChar(cp, 0, data + 3, dwFileSize - 3, NULL, 0);
        wdata = static_cast<WCHAR*>(::HeapAlloc(::GetProcessHeap(), 0, (wlen + 1) * sizeof(WCHAR)));
		::MultiByteToWideChar(cp, 0, data + 3, dwFileSize - 3, wdata, wlen);
		wdata[wlen] = '\0';
	}
	else if (dwFileSize >= 2 && strncmp(data, "\xFE\xFF", 2) == 0) { // UFT16 big endian
		cp = SC_CP_UTF16_Big_Endian;
		wlen = (dwFileSize - 2) / 2;
		wdata = static_cast<WCHAR*>(::HeapAlloc(::GetProcessHeap(), 0, (wlen + 1) * sizeof(WCHAR)));
		for (size_t i = 2, j = 0; j < wlen; i+=2, ++j){
			wdata[j] = (unsigned char)data[i];
			wdata[j] <<= 8;
			wdata[j] |= (unsigned char)data[i + 1];
		}
		wdata[wlen] = '\0';
	}
	else if (dwFileSize >= 2 && strncmp(data, "\xFF\xFE", 2) == 0) { // UTF16 little endian
		cp = SC_CP_UTF16_Little_Endian;
		wlen = (dwFileSize - 2) / 2;
		wdata = static_cast<WCHAR*>(::HeapAlloc(::GetProcessHeap(), 0, (wlen + 1) * sizeof(WCHAR)));
		for (size_t i = 2, j = 0; j < wlen; i+=2, ++j){
			wdata[j] = (unsigned char)data[i + 1];
			wdata[j] <<= 8;
			wdata[j] |= (unsigned char)data[i];
		}
		wdata[wlen] = '\0';
	}
	else{
		cp = SC_CP_UTF8;
		wlen = ::MultiByteToWideChar(cp, 0, data, dwFileSize, NULL, 0);
		wdata = static_cast<WCHAR*>(::HeapAlloc(::GetProcessHeap(), 0, (wlen + 1) * sizeof(WCHAR)));
		::MultiByteToWideChar(cp, 0, data, dwFileSize, wdata, wlen);
		wdata[wlen] = '\0';
	}

    ::HeapFree(::GetProcessHeap(), 0, data);

	*codepage = cp;
	*content = wdata;
	*len = wlen;

	return S_OK;
}

HRESULT Sci_WriteFile(const WCHAR* fileName, WCHAR *content, size_t len, UINT codepage){
	HANDLE hFile = CreateFile(fileName, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, 0, NULL);

	if (hFile == INVALID_HANDLE_VALUE) {
        DWORD err = ::GetLastError();

		return E_FAIL;
	}

    size_t alen = 0;
    char *data = NULL;
    if (codepage == SC_CP_UTF8){
        size_t offset = 3;
        alen = ::WideCharToMultiByte(codepage, NULL, content, len, NULL, NULL, NULL, NULL);
        data = static_cast<char*>(::HeapAlloc(::GetProcessHeap(), 0, alen + offset + 1));
        ::memcpy_s(data, alen, "\xEF\xBB\xBF", 3);
        ::WideCharToMultiByte(codepage, 0, content, len, data + offset, alen, NULL, NULL);
        data[alen + offset] = '\0';
    }
    else if (codepage == SC_CP_UTF16_Big_Endian){
        size_t offset = 2;
        alen = len * 2 + 1;
        data = static_cast<char*>(::HeapAlloc(::GetProcessHeap(), 0, alen + offset + 1));
        ::memcpy_s(data, alen, "\xFE\xFF", 2);
        for (size_t i = 0, j = offset; i < len; ++i, j+=2){
            data[j] = content[i] >> 8;
            data[j + 1] = content[i] & 0x0F;
        }
        data[alen + offset] = '\0';
    }
    else if (codepage == SC_CP_UTF16_Little_Endian){
        size_t offset = 2;
        alen = len * 2 + 1;
        data = static_cast<char*>(::HeapAlloc(::GetProcessHeap(), 0, alen + offset + 1));
        ::memcpy_s(data, alen, "\xFE\xFF", 2);
        for (size_t i = 0, j = offset; i < len; ++i, j+=2){
            data[j] = content[i] & 0x0F;
            data[j + 1] = content[i] >> 8;
        }
        data[alen + offset] = '\0';
    }
    else if (codepage == SC_CP_ACP){
        alen = ::WideCharToMultiByte(codepage, NULL, content, len, NULL, NULL, NULL, NULL);
        data = static_cast<char*>(::HeapAlloc(::GetProcessHeap(), 0, alen + 1));
        ::WideCharToMultiByte(codepage, 0, content, len, data, alen, NULL, NULL);
        data[alen] = '\0';
    }
    else if (codepage == SC_CP_UTF8_NO_BOM){
        alen = ::WideCharToMultiByte(CP_UTF8, NULL, content, len, NULL, NULL, NULL, NULL);
        data = static_cast<char*>(::HeapAlloc(::GetProcessHeap(), 0, alen + 1));
        ::WideCharToMultiByte(CP_UTF8, 0, content, len, data, alen, NULL, NULL);
        data[alen] = '\0';
    }

    DWORD bytesWritten = 0;
    ::WriteFile(hFile, data, alen, &bytesWritten, NULL);
    ::CloseHandle(hFile);

    ::HeapFree(::GetProcessHeap(), 0, data);

	return S_OK;
}